﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Emlyn.AudioToVideo.JobProcessorLib.Interfaces;
using Emlyn.AudioToVideo.JobProcessorLib.Interfaces.Exceptions;
using wandev.Common.Reflection;

namespace Emlyn.AudioToVideo.JobProcessorLib.core
{
    public class JobProcessorImpl
    {
        private static Dictionary<string, IJob> _jobs = new Dictionary<string, IJob>();

        private static IJob GetJobByID(string aJobID)
        {
            IJob ljob;
            if (String.IsNullOrEmpty(aJobID))
                throw new ArgumentNullException("aJobID");

            lock (_jobs)
            {
                if (!_jobs.TryGetValue(aJobID, out ljob))
                    throw new JobDoesNotExistException();
            }

            return ljob;
        }

        // aJobID must not exist, aJobType must make sense, aJobDetails must be for aJobType. State of new job is Created.
        public static int CreateJob(string aJobID, string aJobType, string aJobDetails, out string aErrorMessage)
        {
            int retval = 0;
            aErrorMessage = null;

            try
            {
                if (String.IsNullOrEmpty(aJobID))
                    throw new ArgumentNullException("aJobID");

                if (String.IsNullOrEmpty(aJobType))
                    throw new ArgumentNullException("aJobType");

                if (String.IsNullOrEmpty(aJobDetails))
                    throw new ArgumentNullException("aJobDetails");

                // ensure the job doesn't already exist
                lock (_jobs)
                {
                    if (_jobs.ContainsKey(aJobID))
                        throw new JobAlreadyExistsException();

                    Type ljobType = null;

                    // get the job class type
                    Type[] ljobTypes = Utils.GetConcreteDescendantsFromExecutingAssembly(typeof(JobBase));

                    foreach (Type ljobTypePossibility in ljobTypes)
                    {
                        if (ljobTypePossibility.Name == aJobType)
                        {
                            ljobType = ljobTypePossibility;
                        }
                    }

                    if (ljobType == null)
                        throw new JobTypeDoesNotExistException();

                    IJob ljob = Activator.CreateInstance(ljobType, new object[] { aJobID }) as IJob;

                    ljob.JobDetails = aJobDetails;

                    _jobs.Add(ljob.JobID, ljob);
                }
            }
            catch (JobProcessorExceptionBase jobex)
            {
                retval = jobex.GetErrorCode();
                aErrorMessage = jobex.Message;
            }
            catch (Exception ex)
            {
                retval = int.MaxValue;
                aErrorMessage = ex.Message;
            }

            return retval;
        }

        public static int JobExists(string aJobID, out bool aExists, out string aErrorMessage)
        {
            int retval = 0;
            aErrorMessage = null;
            aExists = false;

            try
            {
                if (String.IsNullOrEmpty(aJobID))
                    throw new ArgumentNullException("aJobID");

                lock (_jobs)
                {
                    aExists = _jobs.ContainsKey(aJobID);
                }
            }
            catch (JobProcessorExceptionBase jobex)
            {
                retval = jobex.GetErrorCode();
                aErrorMessage = jobex.Message;
            }
            catch (Exception ex)
            {
                retval = int.MaxValue;
                aErrorMessage = ex.Message;
            }

            return retval;
        }

        public static int StartJob(string aJobID, out string aErrorMessage)
        {
            int retval = 0;
            aErrorMessage = null;

            try
            {
                IJob ljob = GetJobByID(aJobID);

                ljob.Start();
            }
            catch (JobProcessorExceptionBase jobex)
            {
                retval = jobex.GetErrorCode();
                aErrorMessage = jobex.Message;
            }
            catch (Exception ex)
            {
                retval = int.MaxValue;
                aErrorMessage = ex.Message;
            }

            return retval;
        }

        public static int GetJobStatusAndProgress(string aJobID, out int aJobStatus, out string aJobErrorMessage, out int aProgress, out string aErrorMessage)
        {
            aProgress = -1;
            aJobStatus = -1;
            aJobErrorMessage = String.Empty;
            int retval = 0;
            aErrorMessage = null;

            try
            {
                IJob ljob = GetJobByID(aJobID);

                aJobStatus = (int)ljob.State;
                aProgress = ljob.ProgressAmount;
                aJobErrorMessage = ljob.ErrorMessage;
            }
            catch (JobProcessorExceptionBase jobex)
            {
                retval = jobex.GetErrorCode();
                aErrorMessage = jobex.Message;
            }
            catch (Exception ex)
            {
                retval = int.MaxValue;
                aErrorMessage = ex.Message;
            }

            return retval;
        }

        public static int CancelJob(string aJobID, out string aErrorMessage)
        {
            int retval = 0;
            aErrorMessage = null;

            try
            {
                IJob ljob = GetJobByID(aJobID);

                ljob.Cancel();
            }
            catch (JobProcessorExceptionBase jobex)
            {
                retval = jobex.GetErrorCode();
                aErrorMessage = jobex.Message;
            }
            catch (Exception ex)
            {
                retval = int.MaxValue;
                aErrorMessage = ex.Message;
            }

            return retval;
        }

        public static int DeleteJob(string aJobID, out string aErrorMessage)
        {
            int retval = 0;
            aErrorMessage = null;

            try
            {
                IJob ljob = null;

                lock (_jobs)
                {
                    ljob = GetJobByID(aJobID);

                    _jobs.Remove(aJobID);
                }

                if (ljob is IDisposable)
                    (ljob as IDisposable).Dispose();
            }
            catch (JobProcessorExceptionBase jobex)
            {
                retval = jobex.GetErrorCode();
                aErrorMessage = jobex.Message;
            }
            catch (Exception ex)
            {
                retval = int.MaxValue;
                aErrorMessage = ex.Message;
            }

            return retval;
        }        
    }
}
